package org.deng.learn.basic.nio.socket;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.LinkedList;
import java.util.List;

/**
 * @Project
 * @Module
 * @JDK_Vserion 1.8
 * @Author DengLibin
 * @Email dlb@smarthse.cn
 * @Desc
 *  单线程处理并发, 弊端：即使没有新的连接，已建立的连接没有发数据过来也会一直循环accept和read。（而且accept和read是系统调用）性能低下
 *  思路:
 *  1、accept和read都设置成非阻塞
 *  2、循环中accept获取连接，获取到了放入到集合中 同时遍历集合中的socket read数据
 *  伪代码：
 *  List<<Socket> list;
 *  while(true){
 *      Socket socket = serverSocket.accept(); // 非阻塞
 *      if(socket != null){
 *          list.add(socket);
 *      }
 *      for(Socket socket:list){ // 遍历所有的socket看哪个有数据可读
 *           byte[] buf = new byte[1024];
 *          int len =  socket.read(buf); // 非阻塞
 *          if(len > 0){ // 读到数据
 *              //逻辑处理
 *          }
 *      }
 *  }
 * @since 2019/9/23 0023 12:02
 */
public class SimpleNioServer {
    private List<SocketChannel> clients =  new LinkedList<>();

    private void start() throws IOException, InterruptedException {
        //创建ServerSocket通道
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.socket().bind(new InetSocketAddress(12232));
        //非阻塞
        serverSocketChannel.configureBlocking(false); // 设置为非阻塞，linux下对应的系统的调用为fcntl函数，将socket文件描述符设置成非阻塞
        while (true){
            SocketChannel client = serverSocketChannel.accept(); // 非阻塞 linux下对应的系统调用为accept
            if(client == null){
                System.out.println("no connect");
                Thread.sleep(2000);
            }else{
                client.configureBlocking(false);// 也设置成非阻塞
                System.out.println("new connection:"+client.getRemoteAddress());
                clients.add(client); // 加入到集合 
            }
            // 遍历所有socket
            for(SocketChannel socketChannel : clients){
                ByteBuffer buffer = ByteBuffer.allocate(1024);
                try {
                    int read = socketChannel.read(buffer); // 非阻塞 linux下对应的系统调用为read
                    if(read > 0){ // 读到数据
                        buffer.flip();
                        System.out.println(new String(buffer.array())); // 逻辑处理
                        buffer.clear();
                        buffer.put("hello client".getBytes());
                        buffer.flip();
                        socketChannel.write(buffer);
                    }
                }catch (IOException e){
                    System.out.println(socketChannel.getRemoteAddress()+"断开");
                    clients.remove(socketChannel);
                }


            }

        }
    }


    public static void main(String[] args) throws IOException, InterruptedException {
        SimpleNioServer simpleNioServer = new SimpleNioServer();
        simpleNioServer.start();
    }
}
